"""微服务间通信客户端
提供统一的服务间API调用接口，支持HTTP和Celery
"""
import requests
import json
from typing import Dict, Any, Optional, List, Union, Tuple
from datetime import datetime
import logging
import os
from urllib.parse import urljoin
import traceback

from services.shared.celery_app import celery_app
from services.shared.logger import get_logger

# 使用自定义日志记录器替代标准logging
logger = get_logger('shared.service_client')


class ServiceClient:
    """微服务客户端基类"""
    
    def __init__(self, base_url: str, timeout: int = 30):
        self.base_url = base_url.rstrip('/')
        self.timeout = timeout
        self.session = requests.Session()
        self.session.headers.update({
            'Content-Type': 'application/json',
            'User-Agent': 'XbyJob-ServiceClient/1.0'
        })
    
    def _make_request(self, method: str, endpoint: str, **kwargs) -> Dict[str, Any]:
        """发起HTTP请求"""
        url = f"{self.base_url}{endpoint}"
        logger.debug(f"准备发起HTTP请求: {method} {url}")
        logger.debug(f"请求参数: {kwargs}")
        
        try:
            logger.debug(f"发送HTTP请求: {method} {url}")
            response = self.session.request(
                method=method,
                url=url,
                timeout=self.timeout,
                **kwargs
            )
            
            # 记录请求日志
            logger.info(f"{method} {url} - {response.status_code}")
            logger.debug(f"响应头: {dict(response.headers)}")
            
            if response.status_code == 200:
                logger.debug(f"请求成功: {method} {url} - 200 OK")
                try:
                    result = response.json()
                    logger.debug(f"响应JSON解析成功: {result}")
                    return result
                except json.JSONDecodeError as e:
                    logger.debug(f"响应JSON解析失败: {str(e)}，返回文本内容")
                    return {'success': True, 'data': response.text}
            else:
                error_msg = f"HTTP {response.status_code}: {response.text}"
                logger.error(error_msg)
                logger.debug(f"请求失败详情: {method} {url} - {response.status_code}\n响应内容: {response.text}")
                return {'success': False, 'error': error_msg}
                
        except requests.RequestException as e:
            error_msg = f"请求失败: {str(e)}"
            logger.error(error_msg)
            logger.debug(f"请求异常详情: {traceback.format_exc()}")
            return {'success': False, 'error': error_msg}
    
    def get(self, endpoint: str, params: Optional[Dict] = None) -> Dict[str, Any]:
        """GET请求"""
        return self._make_request('GET', endpoint, params=params)
    
    def post(self, endpoint: str, data: Optional[Dict] = None) -> Dict[str, Any]:
        """POST请求"""
        return self._make_request('POST', endpoint, json=data)
    
    def put(self, endpoint: str, data: Optional[Dict] = None) -> Dict[str, Any]:
        """PUT请求"""
        return self._make_request('PUT', endpoint, json=data)
    
    def delete(self, endpoint: str) -> Dict[str, Any]:
        """DELETE请求"""
        return self._make_request('DELETE', endpoint)
    
    def health_check(self) -> bool:
        """健康检查"""
        result = self.get('/api/health')
        return result.get('success', False)


class SchedulerClient:
    """调度器服务客户端"""
    
    def __init__(self, base_url: str = None, use_celery: bool = True):
        self.use_celery = use_celery
        if not use_celery and base_url:
            self.http_client = ServiceClient(base_url)
        else:
            self.http_client = None
    
    def add_task(self, task_data: Dict[str, Any]) -> Dict[str, Any]:
        """添加任务到调度器"""
        if self.http_client:
            return self.http_client.post('/api/tasks', task_data)
        else:
            # 使用Celery时，直接操作数据库
            return {'success': True, 'message': 'Task added via Celery'}
    
    def remove_task(self, task_id: int) -> Dict[str, Any]:
        """从调度器移除任务"""
        if self.http_client:
            return self.http_client.delete(f'/api/tasks/{task_id}')
        else:
            # 使用Celery时，直接操作数据库
            return {'success': True, 'message': 'Task removed via Celery'}
    
    def update_task(self, task_id: int, task_data: Dict[str, Any]) -> Dict[str, Any]:
        """更新调度器中的任务"""
        if self.http_client:
            return self.http_client.put(f'/api/tasks/{task_id}', task_data)
        else:
            # 使用Celery时，直接操作数据库
            return {'success': True, 'message': 'Task updated via Celery'}
    
    def enable_task(self, task_id: int) -> Dict[str, Any]:
        """启用任务"""
        if self.http_client:
            return self.http_client.post(f'/api/tasks/{task_id}/enable')
        else:
            # 使用Celery时，直接操作数据库
            return {'success': True, 'message': 'Task enabled via Celery'}
    
    def disable_task(self, task_id: int) -> Dict[str, Any]:
        """禁用任务"""
        if self.http_client:
            return self.http_client.post(f'/api/tasks/{task_id}/disable')
        else:
            # 使用Celery时，直接操作数据库
            return {'success': True, 'message': 'Task disabled via Celery'}
    
    def get_scheduler_status(self) -> Dict[str, Any]:
        """获取调度器状态"""
        if self.http_client:
            return self.http_client.get('/api/status')
        else:
            # 当使用Celery时，通过Celery检查状态
            try:
                celery_app.control.ping(timeout=1.0)
                return {'success': True, 'status': 'running', 'using_celery': True}
            except Exception as e:
                logger.error(f"Scheduler Celery健康检查失败: {str(e)}")
                return {'success': False, 'error': str(e)}
    
    def health_check(self) -> bool:
        """健康检查"""
        if self.http_client:
            result = self.http_client.get('/api/health')
            return result.get('success', False)
        else:
            # 当使用Celery时，可以通过ping Celery worker来检查健康状态
            try:
                celery_app.control.ping(timeout=1.0)
                return True
            except Exception as e:
                logger.error(f"Scheduler Celery健康检查失败: {str(e)}")
                return False
    
    def reload_tasks(self) -> Dict[str, Any]:
        """重新加载所有任务"""
        if self.http_client:
            return self.http_client.post('/api/reload')
        else:
            # 使用Celery时，调用reload_tasks任务
            try:
                from services.scheduler.tasks import reload_tasks
                result = reload_tasks.delay()
                return {'success': True, 'task_id': result.id}
            except Exception as e:
                logger.error(f"重新加载任务失败: {str(e)}")
                return {'success': False, 'error': str(e)}
    
    def parse_cron(self, cron_expression: str, count: int = 5) -> Dict[str, Any]:
        """解析Cron表达式"""
        if self.http_client:
            return self.http_client.post('/api/cron/parse', {'expression': cron_expression, 'count': count})
        else:
            # 使用Celery时，直接调用解析函数
            try:
                from services.scheduler.cron_parser import get_next_run_times
                next_times = get_next_run_times(cron_expression, count)
                return {'success': True, 'next_times': next_times}
            except Exception as e:
                logger.error(f"解析Cron表达式失败: {str(e)}")
                return {'success': False, 'error': str(e)}
    
    def get_cron_examples(self) -> Dict[str, Any]:
        """获取Cron表达式示例"""
        return self.get('/api/cron/examples')


class ExecutorClient:
    """执行器服务客户端 - 支持Celery和HTTP
    
    提供统一的任务执行接口，支持通过Celery或HTTP调用执行器服务。
    """
    
    def __init__(self, base_url: str = None, timeout: int = 30, use_celery: bool = True):
        """初始化执行器客户端
        
        Args:
            base_url: HTTP服务基础URL，仅在use_celery=False时使用
            timeout: HTTP请求超时时间（秒）
            use_celery: 是否使用Celery执行任务，默认为True
        """
        self.use_celery = use_celery
        
        # 如果使用HTTP，初始化HTTP客户端
        if not use_celery and base_url:
            self.http_client = ServiceClient(base_url, timeout)
        else:
            self.http_client = None
    
    def execute_task(self, task_data: Dict[str, Any]) -> Dict[str, Any]:
        """执行任务
        
        支持通过Celery或HTTP执行任务。
        
        Args:
            task_data: 任务数据字典，包含任务执行所需的所有信息
            
        Returns:
            包含执行结果的字典
        """
        logger.debug(f"ExecutorClient.execute_task: 开始处理任务执行请求")
        logger.debug(f"ExecutorClient.execute_task: 任务数据: {task_data}")
        
        # 验证任务数据
        if not task_data or not isinstance(task_data, dict):
            error_msg = "任务数据无效或为空"
            logger.error(f"ExecutorClient.execute_task: {error_msg}")
            logger.debug(f"ExecutorClient.execute_task: 任务数据验证失败, 类型: {type(task_data)}")
            return {'success': False, 'error': error_msg}
        
        # 提取关键字段用于日志记录
        task_id = task_data.get('task_id')
        execution_id = task_data.get('execution_id')
        logger.debug(f"ExecutorClient.execute_task: 提取关键字段, task_id={task_id}, execution_id={execution_id}")
        
        if self.use_celery:
            # 使用Celery执行任务
            logger.debug(f"ExecutorClient.execute_task: 使用Celery执行任务")
            try:
                # 添加调试日志
                logger.debug(f"ExecutorClient.execute_task: 准备发送任务到Celery, task_id={task_id}, execution_id={execution_id}")
                logger.info(f"准备发送任务到Celery, task_id={task_id}, execution_id={execution_id}")
                
                # 使用send_task方法调用任务，使用task_data作为关键字参数
                logger.debug(f"ExecutorClient.execute_task: 调用celery_app.send_task, 任务名称='services.executor.tasks.execute_task', 队列='executor'")
                result = celery_app.send_task(
                    'services.executor.tasks.execute_task',
                    args=(),  # 不使用位置参数
                    kwargs={'task_data': task_data},  # 使用关键字参数
                    queue='executor'  # 明确指定使用executor队列
                )
                
                # 添加调试日志
                logger.debug(f"ExecutorClient.execute_task: Celery任务已提交, celery_task_id={result.id}")
                logger.info(f"Celery任务已提交, celery_task_id={result.id}")
                
                return_data = {
                    'success': True,
                    'message': '任务已提交到Celery队列',
                    'celery_task_id': result.id,
                    'task_id': task_id,
                    'execution_id': execution_id
                }
                logger.debug(f"ExecutorClient.execute_task: 返回结果: {return_data}")
                return return_data
            except Exception as e:
                error_msg = f"提交Celery任务失败: {str(e)}"
                logger.error(f"ExecutorClient.execute_task: {error_msg}")
                logger.debug(f"ExecutorClient.execute_task: 异常详情: {traceback.format_exc()}")
                return_data = {'success': False, 'error': error_msg}
                logger.debug(f"ExecutorClient.execute_task: 返回错误结果: {return_data}")
                return return_data
        else:
            # 使用HTTP执行任务
            logger.debug(f"ExecutorClient.execute_task: 使用HTTP执行任务")
            if not self.http_client:
                error_msg = "HTTP客户端未初始化，无法执行任务"
                logger.error(f"ExecutorClient.execute_task: {error_msg}")
                logger.debug(f"ExecutorClient.execute_task: HTTP客户端为None")
                return_data = {'success': False, 'error': error_msg}
                logger.debug(f"ExecutorClient.execute_task: 返回错误结果: {return_data}")
                return return_data
                
            logger.debug(f"ExecutorClient.execute_task: 通过HTTP执行任务, task_id={task_id}, execution_id={execution_id}")
            logger.info(f"通过HTTP执行任务, task_id={task_id}, execution_id={execution_id}")
            result = self.http_client.post('/api/execute', task_data)
            logger.debug(f"ExecutorClient.execute_task: HTTP请求结果: {result}")
            return result
    
    def get_task_status(self, execution_id: str) -> Dict[str, Any]:
        """获取任务状态"""
        if self.http_client:
            return self.http_client.get(f'/api/tasks/{execution_id}/status')
        else:
            # 当使用Celery时，状态查询应该通过数据库完成
            logger.warning("使用Celery时，任务状态应通过数据库查询")
            return {'success': False, 'error': '使用Celery时，任务状态应通过数据库查询'}
    
    def stop_task(self, execution_id: str) -> Dict[str, Any]:
        """停止任务"""
        if self.http_client:
            return self.http_client.post(f'/api/tasks/{execution_id}/stop')
        else:
            # 当使用Celery时，调用executor的停止任务功能
            try:
                logger.debug(f"ExecutorClient.stop_task: 使用Celery停止任务: {execution_id}")
                from services.executor.tasks import stop_task
                
                # 异步调用停止任务
                result = stop_task.delay(execution_id)
                
                # 等待结果（设置较短的超时时间）
                try:
                    task_result = result.get(timeout=10)
                    logger.debug(f"ExecutorClient.stop_task: 停止任务结果: {task_result}")
                    return task_result
                except Exception as e:
                    # 如果异常是CANCELLED，这表示任务已被成功取消
                    if 'CANCELLED' in str(e).upper():
                        logger.info(f"ExecutorClient.stop_task: 任务已被取消: {execution_id}")
                        return {'success': True, 'message': '任务已停止'}
                    else:
                        logger.warning(f"ExecutorClient.stop_task: 等待停止任务结果超时: {str(e)}")
                        # 即使超时，也返回成功，因为停止命令已经发送
                        return {'success': True, 'message': '停止命令已发送'}
                    
            except Exception as e:
                error_msg = f"停止任务失败: {str(e)}"
                logger.error(f"ExecutorClient.stop_task: {error_msg}")
                return {'success': False, 'error': error_msg}
    
    def get_executor_status(self) -> Dict[str, Any]:
        """获取执行器状态"""
        if self.http_client:
            return self.http_client.get('/api/status')
        else:
            # 当使用Celery时，可以通过Celery的API获取状态
            # 但这需要额外的实现
            return {'success': True, 'status': 'running', 'using_celery': True}
    
    def get_running_tasks(self) -> Dict[str, Any]:
        """获取正在运行的任务"""
        try:
            # 导入数据库模型
            from services.shared.database import TaskExecution, Task
            from services.shared.db_pool import get_db_session, close_db_session
            from services.shared.models.enums import TaskStatus
            
            session = get_db_session()
            try:
                # 查询所有正在运行的任务执行记录
                running_executions = session.query(TaskExecution).filter_by(status=TaskStatus.RUNNING).all()
                
                tasks_data = []
                for execution in running_executions:
                    # 获取关联的任务信息
                    task = session.query(Task).get(execution.task_id)
                    task_name = task.name if task else "未知任务"
                    
                    tasks_data.append({
                        'execution_id': execution.execution_id,
                        'task_id': execution.task_id,
                        'task_name': task_name,
                        'started_at': execution.started_at.isoformat() if execution.started_at else None,
                        'status': execution.status.value if execution.status else 'unknown',
                        'executor_pid': execution.executor_pid
                    })
                
                return {
                    'success': True,
                    'data': tasks_data,
                    'count': len(tasks_data)
                }
            finally:
                close_db_session()
                
        except Exception as e:
            logger.error(f"获取正在运行的任务失败: {str(e)}")
            return {
                'success': False,
                'error': f'获取正在运行的任务失败: {str(e)}',
                'data': []
            }
    
    def health_check(self) -> bool:
        """健康检查"""
        if self.http_client:
            result = self.http_client.get('/api/health')
            return result.get('success', False)
        else:
            # 当使用Celery时，可以通过ping Celery worker来检查健康状态
            try:
                celery_app.control.ping(timeout=1.0)
                return True
            except Exception as e:
                logger.error(f"Celery健康检查失败: {str(e)}")
                return False
    
    def list_dependencies(self) -> Dict[str, Any]:
        """获取已安装的依赖包列表"""
        if self.http_client:
            return self.http_client.get('/api/dependencies')
        else:
            # 当使用Celery时，直接调用HTTP接口
            try:
                from config.config import get_config
                config = get_config()
                # 在Docker环境中使用容器名，本地开发使用localhost
                import os
                if os.getenv('FLASK_ENV') == 'production':
                    # Docker环境中使用celery-workers容器的依赖管理API
                    base_url = f"http://celery-workers:5004"
                else:
                    base_url = f"http://localhost:5013"
                http_client = ServiceClient(base_url)
                return http_client.get('/api/dependencies')
            except Exception as e:
                logger.error(f"获取依赖包列表失败: {str(e)}")
                return {'success': False, 'error': f'获取依赖包列表失败: {str(e)}'}
    
    def uninstall_dependency(self, package_name: str) -> Dict[str, Any]:
        """卸载依赖包"""
        if self.http_client:
            return self.http_client.delete(f'/api/dependencies/{package_name}')
        else:
            # 当使用Celery时，直接调用HTTP接口
            try:
                from config.config import get_config
                config = get_config()
                # 在Docker环境中使用容器名，本地开发使用localhost
                import os
                if os.getenv('FLASK_ENV') == 'production':
                    # Docker环境中使用celery-workers容器的依赖管理API
                    base_url = f"http://celery-workers:5004"
                else:
                    base_url = f"http://localhost:5013"
                http_client = ServiceClient(base_url)
                return http_client.delete(f'/api/dependencies/{package_name}')
            except Exception as e:
                logger.error(f"卸载依赖包失败: {str(e)}")
                return {'success': False, 'error': f'卸载依赖包失败: {str(e)}'}
                
    def install_dependency(self, package: str, version: Optional[str] = None, 
                         index_url: Optional[str] = None, trusted_host: bool = False) -> Dict[str, Any]:
        """安装依赖包"""
        data = {
            'package': package
        }
        
        if version:
            data['version'] = version
            
        if index_url:
            data['index_url'] = index_url
            
        if trusted_host:
            data['trusted_host'] = trusted_host
            
        if self.http_client:
            return self.http_client.post('/api/dependencies/install', data)
        else:
            # 当使用Celery时，直接调用HTTP接口
            try:
                from config.config import get_config
                config = get_config()
                # 在Docker环境中使用容器名，本地开发使用localhost
                import os
                if os.getenv('FLASK_ENV') == 'production':
                    # Docker环境中使用celery-workers容器的依赖管理API
                    base_url = f"http://celery-workers:5004"
                else:
                    base_url = f"http://localhost:5013"
                http_client = ServiceClient(base_url)
                return http_client.post('/api/dependencies/install', data)
            except Exception as e:
                logger.error(f"安装依赖包失败: {str(e)}")
                return {'success': False, 'error': f'安装依赖包失败: {str(e)}'}


class ServiceRegistry:
    """服务注册表"""
    
    def __init__(self, config):
        self.config = config
        self._clients = {}
        self.use_celery = True  # 默认使用Celery
    
    def get_scheduler_client(self) -> SchedulerClient:
        """获取调度器客户端"""
        if 'scheduler' not in self._clients:
            if self.use_celery:
                # 使用Celery模式
                self._clients['scheduler'] = SchedulerClient(use_celery=True)
            else:
                # 使用HTTP模式（本地开发）
                base_url = f"http://localhost:{self.config.SCHEDULER_SERVICE_PORT}"
                self._clients['scheduler'] = SchedulerClient(base_url, use_celery=False)
        return self._clients['scheduler']
    
    def get_executor_client(self) -> ExecutorClient:
        """获取执行器客户端"""
        if 'executor' not in self._clients:
            if self.use_celery:
                # 使用Celery
                self._clients['executor'] = ExecutorClient(use_celery=True)
            else:
                # 使用HTTP
                # 在Docker环境中使用容器名，本地开发使用localhost
                import os
                if os.getenv('FLASK_ENV') == 'production':
                    # Docker环境中使用容器名和内部端口
                    base_url = f"http://executor-api:{self.config.EXECUTOR_SERVICE_PORT}"
                else:
                    base_url = f"http://localhost:{self.config.EXECUTOR_SERVICE_PORT}"
                self._clients['executor'] = ExecutorClient(base_url, use_celery=False)
        return self._clients['executor']
    
    def health_check_all(self) -> Dict[str, bool]:
        """检查所有服务健康状态"""
        return {
            'scheduler': self.get_scheduler_client().health_check(),
            'executor': self.get_executor_client().health_check()
        }
    
    def get_services_status(self) -> Dict[str, Dict[str, Any]]:
        """获取所有服务状态"""
        return {
            'scheduler': self.get_scheduler_client().get_scheduler_status(),
            'executor': self.get_executor_client().get_executor_status()
        }


# 全局服务注册表实例
_service_registry = None


def get_service_registry():
    """获取服务注册表实例"""
    global _service_registry
    if _service_registry is None:
        from config.config import get_config
        _service_registry = ServiceRegistry(get_config())
    return _service_registry


def get_scheduler_client() -> SchedulerClient:
    """获取调度器客户端"""
    return get_service_registry().get_scheduler_client()


def get_executor_client() -> ExecutorClient:
    """获取执行器客户端"""
    return get_service_registry().get_executor_client()